
package olympicgameslogic;



import java.io.Serializable;
import java.util.*;



public class LinkedList<T> implements List<T>, Serializable {
	
    private Node<T> head = new Node();      
    private Node<T> tail = new Node();       
    private int size;                           
    
    
    public LinkedList() {
        head.setPrevious(null);head.setNext(tail);
        tail.setPrevious(head);tail.setNext(null);
        size = 0;
    }
     
    
    
    @Override
    public int size() {
        return size;
    }
    
    public int getSize(){
       return size;
    }

    
    
    @Override
    public boolean isEmpty() {
        return size==0;
    }

    
    @Override
    public boolean contains(Object element) {
       Node node = head.getNext();
       for (int i=0;i<size;i++){
           if (node.getElement()== element) return true;
           node = node.getNext();               
       }
       return false;                     
    }


    @Override
    public boolean add(T e) {
        Node<T> node = tail.getPrevious(); 
        Node<T> newNode = new Node(e,node,node.getNext()); 
        node.getNext().setPrevious(newNode);
	node.setNext(newNode);
	size++;        
        return true;
    }
    
   
    @Override
    public boolean remove(Object o) {
        if (size==0) return false;
        Node node =  head.getNext();
        for (int i=0;i<size;i++){
            if (node.getElement() == o){          
                node.getNext().setPrevious(node.getPrevious());
                node.getPrevious().setNext(node.getNext());
                size--;                
                return true;
            }                                       
            node = node.getNext();
        }
        return false;
    }    

    
    @Override
    public void clear() {
        head.setPrevious(null);head.setNext(tail);
        tail.setPrevious(head);tail.setNext(null);
        size = 0;
    }

    
    @Override
    public T get(int index) {
        int idx=0;
        
        if (size==0 || size<index+1) return null;               
        
        Node<T> node =  head.getNext();        
        for (int i=0;i<index;i++)
            node = node.getNext();
        
        return node.getElement();
    }
    public T getElement(int index) {
        int idx=0;
        
        if (size==0 || size<index+1) return null;               
        
        Node<T> node =  head.getNext();        
        for (int i=0;i<index;i++)
            node = node.getNext();
        
        return node.getElement();
    }

    
    public T getLast() {
        if (size==0) return null;               
        return tail.getPrevious().getElement();
    }
    
    
    @Override
    public T remove(int index) {
       if (size<=index) 
           throw new ArrayIndexOutOfBoundsException("Invalid index: " + index);
       
       Node<T> node = head.getNext();
       for (int i=0;i<index;i++){
           node = node.getNext();               
       }
       node.getNext().setPrevious(node.getPrevious());
       node.getPrevious().setNext(node.getNext());
       size--;       
       return node.getElement();
    }
      
    @Override
    public Object[] toArray() {
        Object[] list = new Object[size];
        Node<T> node =  head.getNext();
        for (int i=0;i<size;i++){
            list[i]= node.getElement();
            node = node.getNext();
        }
        return list;
    }
    
   
    @Override
    public int indexOf(Object o) {
       if (size == 0) return -1;
       Node<T> node = head.getNext();
       for (int i=0;i<size;i++){
           if (node.getElement()==o) return i;
           node = node.getNext();               
       }
       return -1;
    }
    
   
    @Override
    public int lastIndexOf(Object o) {
       if (size == 0) return -1;
       Node<T> node = tail.getPrevious();
       for (int i=size-1;i>=0;i--){
           if (node.getElement()==o) return i;
           node = node.getPrevious();               
       }
       return -1;
    }
    
    
    @Override
    public String toString(){
        String s = "";
        Node<T> node =  head.getNext();        
        for (int i=0;i<size;i++){
            s = s + "\n" + node.getElement().toString();
            node = node.getNext();
        }
        return s; 
    }

   
    
   
   
        
    private class Node<T> implements Serializable
     {
        private T element;                          
        private Node<T> next, previous;            
    
       
        public Node(T o, Node<T> previous,Node<T> next) {
            element = o;
            this.next = next;
            this.previous = previous;
        }
        
        
        public Node() {
            this(null, null,null); 
        }          
        
             
        public Node(T o) {
            this(o, null,null); 
        }
        
      
        public T getElement() {
            return element;
        }
        
        
        public void setElement(T newElement) {
            this.element = newElement;
        }
        
        
        public void setNext(Node<T> no) {
            this.next = no;
        }

        
        public Node<T> getNext() {
            return next;
        }
        
       
        public void setPrevious(Node<T> no) {
            this.previous = no;
        }
        
        
        public Node<T> getPrevious() {
            return previous;
        }

        
        
        @Override
        public String toString (){
            String s = "";
            if (this.getElement()!=null)
                s = "Element: " + this.getElement().toString(); 
            else
                s = "Element: None";
            
            return s;
        }
      }
     
        
     
    @Override
    public Iterator<T> iterator() {
        return new LinkedIterator();
    }

      
    private class LinkedIterator implements Iterator,Serializable {
        
        final LinkedListIterator itr = new LinkedListIterator();
        
      
        @Override
	public boolean hasNext() {
	    return itr.hasNext();
	}
        
        
       
        @Override
	public T next() {
            return itr.next();
        }
        
      
        @Override
	public void remove() {
            itr.remove();
        }
    }
         
    
    @Override
    public ListIterator<T> listIterator() {
        return new LinkedListIterator();
    }
    
   
    @Override
    public ListIterator<T> listIterator(int index) {
        return new LinkedListIterator(index);        
    }

     
    public class LinkedListIterator implements ListIterator<T> , Serializable{
	private Node<T> lastReturned = head;        
	private Node<T> next;                       
	private int nextIndex;                    

        
        public LinkedListIterator() {
            this(0);
        }       
        
        
	LinkedListIterator (int index) {
            
	    if (index < 0 || (index > size-1 && size!=0) )
		throw new IndexOutOfBoundsException("The element at index " + index + " doesn't exist!");
            
	    next = head.getNext();
            for (nextIndex=0; nextIndex<index; nextIndex++)
	       next = next.getNext();
	   
	}
       
        @Override
	public boolean hasNext() {
	    return nextIndex < size ; 
	}
        
       
        @Override
	public T next() {
	    if (nextIndex == size)
		throw new NoSuchElementException("No such element");

	    lastReturned = next;
	    next = next.getNext();
	    nextIndex++;
	    return lastReturned.getElement();
	}
        
      
        @Override
	public boolean hasPrevious() {
	    return nextIndex != 0;
	}
        
       
        @Override
	public T previous() {
	    if (nextIndex == 0)
		throw new NoSuchElementException("No such element");

	    lastReturned = next = next.getPrevious();
	    nextIndex--;	    
	    return lastReturned.getElement();
	}
        
      
        @Override
	public int nextIndex() {
	    return nextIndex;
	}
        
      
        @Override
	public int previousIndex() {
	    return nextIndex-1;
	}
        
      
        @Override
	public void set(T e) {
	    if (lastReturned == head)
		throw new IllegalStateException();
	    lastReturned.setElement(e);
	}
        
       
        @Override
	public void add(T e) {
            throw new UnsupportedOperationException("Not Supported");
	}	
      
        @Override
	public void remove() {
            throw new UnsupportedOperationException("Not Supported");    
	}

        
    }
    
    
    /*****************************
     not implemented 
     ****************************/
    @Override
    public <T> T[] toArray(T[] a) {
        throw new UnsupportedOperationException("Not Supported");
    }

    
    @Override
    public boolean containsAll(Collection<?> c) {
        throw new UnsupportedOperationException("Not Supported");
    }

    
    @Override
    public boolean addAll(Collection<? extends T> c) {
        throw new UnsupportedOperationException("Not Supported");
    }
    
   
    @Override
    public boolean addAll(int index, Collection<? extends T> c) {
        throw new UnsupportedOperationException("Not Supported");
    }
    
   
    @Override
    public boolean removeAll(Collection<?> c) {
        throw new UnsupportedOperationException("Not Supported"); 
    }
    
   
    @Override
    public boolean retainAll(Collection<?> c) {
        throw new UnsupportedOperationException("Not Supported");
    }
   
    @Override
    public T set(int index, T element) {
        throw new UnsupportedOperationException("Not Supported");
    }
    
   
    @Override
    public void add(int index, T element) {
        throw new UnsupportedOperationException("Not Supported");
    }
    
    
    @Override
    public List<T> subList(int fromIndex, int toIndex) {
        throw new UnsupportedOperationException("Not Supported");
    }
}

